package com.gitee.wsl.mathematics.function.random

import com.gitee.wsl.collections.map.TreeMap
import com.gitee.wsl.math.random.weighted
import kotlin.random.Random

/**
 * 权重随机算法实现<br></br>
 *
 *
 * 平时，经常会遇到权重随机算法，从不同权重的N个元素中随机选择一个，并使得总体选择结果是按照权重分布的。如广告投放、负载均衡等。
 *
 *
 *
 * 如有4个元素A、B、C、D，权重分别为1、2、3、4，随机结果中A:B:C:D的比例要为1:2:3:4。<br></br>
 *
 * 总体思路：累加每个元素的权重A(1)-B(3)-C(6)-D(10)，则4个元素的的权重管辖区间分别为[0,1)、[1,3)、[3,6)、[6,10)。<br></br>
 * 然后随机出一个[0,10)之间的随机数。落在哪个区间，则该区间之后的元素即为按权重命中的元素。<br></br>
 *
 *
 *
 * 参考博客：https://www.cnblogs.com/waterystone/p/5708063.html
 *
 *
 *
 * @param <T> 权重随机获取的对象类型
 * @author looly
 * @since 3.3.0
</T> */
class WeightRandom<T:Comparable<T>>()  {

    private val weightMap = TreeMap<Double, T>()

    // ---------------------------------------------------------------------------------- Constructor start

    /**
     * 构造
     *
     * @param weightObj 带有权重的对象
     */
    constructor(weightObj: WeightObj<T>) : this() {
       add(weightObj)
    }

    /**
     * 构造
     *
     * @param weightObjs 带有权重的对象
     */
    constructor(weightObjs: List<WeightObj<T>>) : this() {
        if (weightObjs.isNotEmpty()) {
            for (weightObj in weightObjs) {
                add(weightObj)
            }
        }
    }

    /**
     * 构造
     *
     * @param weightObjs 带有权重的对象
     */
    constructor(weightObjs: Array<WeightObj<T>>) : this() {
        for (weightObj in weightObjs) {
            add(weightObj)
        }
    }

    // ---------------------------------------------------------------------------------- Constructor end
    /**
     * 增加对象
     *
     * @param obj 对象
     * @param weight 权重
     * @return this
     */
    fun add(obj: T, weight: Double): WeightRandom<T> {
        return add(WeightObj(obj, weight))
    }

    /**
     * 增加对象权重
     *
     * @param weightObj 权重对象
     * @return this
     */
    fun add(weightObj: WeightObj<T>): WeightRandom<T> {
            val weight = weightObj.weight
            if (weightObj.weight > 0) {
                val lastWeight = if (this.weightMap.isEmpty()) 0.0 else this.weightMap.lastKey()?:0.0
                this.weightMap.put(weight + lastWeight, weightObj.obj) // 权重累加
            }
        return this
    }

    /**
     * 清空权重表
     *
     * @return this
     */
    fun clear(): WeightRandom<T> {
        this.weightMap.clear()
        return this
    }

    /**
     * 下一个随机对象
     *
     * @return 随机对象
     */
    fun next(): T? {
        if (weightMap.isEmpty()) {
            return null
        }

//        val randomWeight = this.weightMap.lastKey() * Random.nextDouble()
//        val tailMap = this.weightMap.tailMap(randomWeight, false)
//        return this.weightMap[tailMap.firstKey()]
        return Random.weighted(weightMap.values.toList(),weightMap.keys.toList())
    }

    /**
     * 带有权重的对象包装
     *
     * @author looly
     *
     * @param <T> 对象类型
    </T> */
    class WeightObj<T>(
        /** 对象  */
        var obj: T,
        /** 权重  */
        val weight: Double,
    ) {
        /**
         * 获取对象
         *
         * @return 对象
         */
        /**
         * 设置对象
         *
         * @param obj 对象
         */
        /**
         * 获取权重
         *
         * @return 权重
         */

        override fun hashCode(): Int {
            val prime = 31
            var result = 1
            result = prime * result + (if (obj == null) 0 else obj.hashCode())
            val temp: Long = weight.toBits()
            result = prime * result + (temp xor (temp ushr 32)).toInt()
            return result
        }

        override fun equals(obj: Any?): Boolean {
            if (this === obj) {
                return true
            }
            if (obj == null) {
                return false
            }
            if (this::class != obj::class) {
                return false
            }
            val other = obj as WeightObj<*>
            if (this.obj == null) {
                if (other.obj != null) {
                    return false
                }
            } else if (this.obj != other.obj) {
                return false
            }
            return weight.toBits() == other.weight.toBits()
        }
    }

    companion object {

        /**
         * 创建权重随机获取器
         *
         * @param <T> 权重随机获取的对象类型
         * @return [WeightRandom]
        </T> */
        fun <T:Comparable<T>> create(): WeightRandom<T> {
            return WeightRandom()
        }
    }
}
